package com.jeeplus.mob.payUtile;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Map;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;

public class WXPay
{
  private WXPayConfig config;
  private WXPayConstants.SignType signType;
  private boolean useSandbox;
  
  public WXPay(WXPayConfig config)
  {
    this(config, WXPayConstants.SignType.MD5, false);
  }
  
  public WXPay(WXPayConfig config, WXPayConstants.SignType signType)
  {
    this(config, signType, false);
  }
  
  public WXPay(WXPayConfig config, WXPayConstants.SignType signType, boolean useSandbox)
  {
    this.config = config;
    this.signType = signType;
    this.useSandbox = useSandbox;
  }
  
  public Map<String, String> fillRequestData(Map<String, String> reqData)
    throws Exception
  {
    if (!WXPayConstants.SignType.MD5.equals(this.signType)) {
      if (WXPayConstants.SignType.HMACSHA256.equals(this.signType)) {
        reqData.put("sign_type", "HMAC-SHA256");
      }
    }
    return reqData;
  }
  
  public boolean isResponseSignatureValid(Map<String, String> reqData)
    throws Exception
  {
    return WXPayUtil.isSignatureValid(reqData, this.config.getKey(), this.signType);
  }
  
  public boolean isPayResultNotifySignatureValid(Map<String, String> reqData)
    throws Exception
  {
    String signTypeInData = (String)reqData.get("sign_type");
    WXPayConstants.SignType signType;
    if (signTypeInData == null)
    {
      signType = WXPayConstants.SignType.MD5;
    }
    else
    {
      signTypeInData = signTypeInData.trim();
      if (signTypeInData.length() == 0)
      {
        signType = WXPayConstants.SignType.MD5;
      }
      else
      {
        if ("MD5".equals(signTypeInData))
        {
          signType = WXPayConstants.SignType.MD5;
        }
        else
        {
          if ("HMAC-SHA256".equals(signTypeInData)) {
            signType = WXPayConstants.SignType.HMACSHA256;
          } else {
            throw new Exception(String.format("Unsupported sign_type: %s", new Object[] { signTypeInData }));
          }
        }
      }
    }
    return WXPayUtil.isSignatureValid(reqData, this.config.getKey(), signType);
  }
  
  public String requestWithoutCert(String strUrl, Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs)
    throws Exception
  {
    String UTF8 = "UTF-8";
    String reqBody = PayCommonUtil.getRequestXml(reqData);
    
    URL httpUrl = new URL(strUrl);
    HttpURLConnection httpURLConnection = (HttpURLConnection)httpUrl.openConnection();
    httpURLConnection.setDoOutput(true);
    httpURLConnection.setRequestMethod("POST");
    httpURLConnection.setConnectTimeout(connectTimeoutMs);
    httpURLConnection.setReadTimeout(readTimeoutMs);
    httpURLConnection.connect();
    OutputStream outputStream = httpURLConnection.getOutputStream();
    outputStream.write(reqBody.getBytes(UTF8));
    




    InputStream inputStream = httpURLConnection.getInputStream();
    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, UTF8));
    StringBuffer stringBuffer = new StringBuffer();
    String line = null;
    while ((line = bufferedReader.readLine()) != null) {
      stringBuffer.append(line);
    }
    String resp = stringBuffer.toString();
    if (stringBuffer != null) {
      try
      {
        bufferedReader.close();
      }
      catch (IOException e)
      {
        e.printStackTrace();
      }
    }
    if (inputStream != null) {
      try
      {
        inputStream.close();
      }
      catch (IOException e)
      {
        e.printStackTrace();
      }
    }
    if (outputStream != null) {
      try
      {
        outputStream.close();
      }
      catch (IOException e)
      {
        e.printStackTrace();
      }
    }
    return resp;
  }
  
  public String requestWithCert(String strUrl, Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs)
    throws Exception
  {
    String UTF8 = "UTF-8";
    String reqBody = WXPayUtil.mapToXml(reqData);
    URL httpUrl = new URL(strUrl);
    char[] password = this.config.getMchID().toCharArray();
    InputStream certStream = this.config.getCertStream();
    KeyStore ks = KeyStore.getInstance("PKCS12");
    ks.load(certStream, password);
    

    KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
    kmf.init(ks, password);
    

    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(kmf.getKeyManagers(), null, new SecureRandom());
    HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
    
    HttpURLConnection httpURLConnection = (HttpURLConnection)httpUrl.openConnection();
    
    httpURLConnection.setDoOutput(true);
    httpURLConnection.setRequestMethod("POST");
    httpURLConnection.setConnectTimeout(connectTimeoutMs);
    httpURLConnection.setReadTimeout(readTimeoutMs);
    httpURLConnection.connect();
    OutputStream outputStream = httpURLConnection.getOutputStream();
    outputStream.write(reqBody.getBytes(UTF8));
    





    InputStream inputStream = httpURLConnection.getInputStream();
    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, UTF8));
    StringBuffer stringBuffer = new StringBuffer();
    String line = null;
    while ((line = bufferedReader.readLine()) != null) {
      stringBuffer.append(line);
    }
    String resp = stringBuffer.toString();
    if (stringBuffer != null) {
      try
      {
        bufferedReader.close();
      }
      catch (IOException localIOException) {}
    }
    if (inputStream != null) {
      try
      {
        inputStream.close();
      }
      catch (IOException localIOException1) {}
    }
    if (outputStream != null) {
      try
      {
        outputStream.close();
      }
      catch (IOException localIOException2) {}
    }
    if (certStream != null) {
      try
      {
        certStream.close();
      }
      catch (IOException localIOException3) {}
    }
    return resp;
  }
  
  public Map<String, String> processResponseXml(String xmlStr)
    throws Exception
  {
    String RETURN_CODE = "return_code";
    
    Map<String, String> respData = WXPayUtil.xmlToMap(xmlStr);
    String return_code;
    if (respData.containsKey(RETURN_CODE)) {
      return_code = (String)respData.get(RETURN_CODE);
    } else {
      throw new Exception(String.format("No `return_code` in XML: %s", new Object[] { xmlStr }));
    }
    if (return_code.equals("FAIL")) {
      return respData;
    }
    if (return_code.equals("SUCCESS"))
    {
      if (isResponseSignatureValid(respData)) {
        return respData;
      }
      throw new Exception(String.format("Invalid sign value in XML: %s", new Object[] { xmlStr }));
    }
    throw new Exception(String.format("return_code value %s is invalid in XML: %s", new Object[] { return_code, xmlStr }));
  }
  
  public Map<String, String> microPay(Map<String, String> reqData)
    throws Exception
  {
    return microPay(reqData, this.config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs());
  }
  
  public Map<String, String> microPay(Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs)
    throws Exception
  {
    String url;
    if (this.useSandbox) {
      url = "https://api.mch.weixin.qq.com/sandboxnew/pay/micropay";
    } else {
      url = "https://api.mch.weixin.qq.com/pay/micropay";
    }
    String respXml = requestWithoutCert(url, fillRequestData(reqData), connectTimeoutMs, readTimeoutMs);
    return processResponseXml(respXml);
  }
  
  public Map<String, String> unifiedOrder(Map<String, String> reqData)
    throws Exception
  {
    return unifiedOrder(reqData, this.config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs());
  }
  
  public Map<String, String> unifiedOrder(Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs)
    throws Exception
  {
    String url;
    if (this.useSandbox) {
      url = "https://api.mch.weixin.qq.com/sandboxnew/pay/unifiedorder";
    } else {
      url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
    }
    String respXml = requestWithoutCert(url, fillRequestData(reqData), connectTimeoutMs, readTimeoutMs);
    return processResponseXml(respXml);
  }
  
  public Map<String, String> orderQuery(Map<String, String> reqData)
    throws Exception
  {
    return orderQuery(reqData, this.config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs());
  }
  
  public Map<String, String> orderQuery(Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs)
    throws Exception
  {
    String url;
    if (this.useSandbox) {
      url = "https://api.mch.weixin.qq.com/sandboxnew/pay/orderquery";
    } else {
      url = "https://api.mch.weixin.qq.com/pay/orderquery";
    }
    String respXml = requestWithoutCert(url, fillRequestData(reqData), connectTimeoutMs, readTimeoutMs);
    return processResponseXml(respXml);
  }
  
  public Map<String, String> reverse(Map<String, String> reqData)
    throws Exception
  {
    return reverse(reqData, this.config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs());
  }
  
  public Map<String, String> reverse(Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs)
    throws Exception
  {
    String url;
    if (this.useSandbox) {
      url = "https://api.mch.weixin.qq.com/sandboxnew/secapi/pay/reverse";
    } else {
      url = "https://api.mch.weixin.qq.com/secapi/pay/reverse";
    }
    String respXml = requestWithCert(url, fillRequestData(reqData), connectTimeoutMs, readTimeoutMs);
    return processResponseXml(respXml);
  }
  
  public Map<String, String> closeOrder(Map<String, String> reqData)
    throws Exception
  {
    return closeOrder(reqData, this.config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs());
  }
  
  public Map<String, String> closeOrder(Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs)
    throws Exception
  {
    String url;
    if (this.useSandbox) {
      url = "https://api.mch.weixin.qq.com/sandboxnew/pay/closeorder";
    } else {
      url = "https://api.mch.weixin.qq.com/pay/closeorder";
    }
    String respXml = requestWithoutCert(url, fillRequestData(reqData), connectTimeoutMs, readTimeoutMs);
    return processResponseXml(respXml);
  }
  
  public Map<String, String> refund(Map<String, String> reqData)
    throws Exception
  {
    return refund(reqData, this.config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs());
  }
  
  public Map<String, String> refund(Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs)
    throws Exception
  {
    String url;
    if (this.useSandbox) {
      url = "https://api.mch.weixin.qq.com/sandboxnew/secapi/pay/refund";
    } else {
      url = "https://api.mch.weixin.qq.com/secapi/pay/refund";
    }
    String respXml = requestWithCert(url, fillRequestData(reqData), connectTimeoutMs, readTimeoutMs);
    return processResponseXml(respXml);
  }
  
  public Map<String, String> refundQuery(Map<String, String> reqData)
    throws Exception
  {
    return refundQuery(reqData, this.config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs());
  }
  
  public Map<String, String> refundQuery(Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs)
    throws Exception
  {
    String url;
    if (this.useSandbox) {
      url = "https://api.mch.weixin.qq.com/sandboxnew/pay/refundquery";
    } else {
      url = "https://api.mch.weixin.qq.com/pay/refundquery";
    }
    String respXml = requestWithoutCert(url, fillRequestData(reqData), connectTimeoutMs, readTimeoutMs);
    return processResponseXml(respXml);
  }
  
  public Map<String, String> downloadBill(Map<String, String> reqData)
    throws Exception
  {
    return downloadBill(reqData, this.config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs());
  }
  
  public Map<String, String> downloadBill(Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs)
    throws Exception
  {
    String url;
    if (this.useSandbox) {
      url = "https://api.mch.weixin.qq.com/sandboxnew/pay/downloadbill";
    } else {
      url = "https://api.mch.weixin.qq.com/pay/downloadbill";
    }
    String respStr = requestWithoutCert(url, fillRequestData(reqData), connectTimeoutMs, readTimeoutMs).trim();
    Map<String, String> ret;
    if (respStr.indexOf("<") == 0)
    {
      ret = WXPayUtil.xmlToMap(respStr);
    }
    else
    {
      ret = new HashMap();
      ret.put("return_code", "SUCCESS");
      ret.put("return_msg", "ok");
      ret.put("data", respStr);
    }
    return ret;
  }
  
  public Map<String, String> report(Map<String, String> reqData)
    throws Exception
  {
    return report(reqData, this.config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs());
  }
  
  public Map<String, String> report(Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs)
    throws Exception
  {
    String url;
    if (this.useSandbox) {
      url = "https://api.mch.weixin.qq.com/sandboxnew/payitil/report";
    } else {
      url = "https://api.mch.weixin.qq.com/payitil/report";
    }
    String respXml = requestWithoutCert(url, fillRequestData(reqData), connectTimeoutMs, readTimeoutMs);
    return WXPayUtil.xmlToMap(respXml);
  }
  
  public Map<String, String> shortUrl(Map<String, String> reqData)
    throws Exception
  {
    return shortUrl(reqData, this.config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs());
  }
  
  public Map<String, String> shortUrl(Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs)
    throws Exception
  {
    String url;
    if (this.useSandbox) {
      url = "https://api.mch.weixin.qq.com/sandboxnew/tools/shorturl";
    } else {
      url = "https://api.mch.weixin.qq.com/tools/shorturl";
    }
    String respXml = requestWithoutCert(url, fillRequestData(reqData), connectTimeoutMs, readTimeoutMs);
    return processResponseXml(respXml);
  }
  
  public Map<String, String> authCodeToOpenid(Map<String, String> reqData)
    throws Exception
  {
    return authCodeToOpenid(reqData, this.config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs());
  }
  
  public Map<String, String> authCodeToOpenid(Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs)
    throws Exception
  {
    String url;
    if (this.useSandbox) {
      url = "https://api.mch.weixin.qq.com/sandboxnew/tools/authcodetoopenid";
    } else {
      url = "https://api.mch.weixin.qq.com/tools/authcodetoopenid";
    }
    String respXml = requestWithoutCert(url, fillRequestData(reqData), connectTimeoutMs, readTimeoutMs);
    return processResponseXml(respXml);
  }
}
